﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once

//定义剪贴板管理类
#include "GbmpModel.h"
#include "IClipBoard.h"
#include "IDocumentEventHandler.h"
#include "Box3d.h"
#include "Coordinate3d.h"

namespace gcmp
{
    class IDocument;
    class IFamily;
    class Box3d;
    class Coordinate3d;
    class IElementsChangeEventHandler;
    typedef IFamily* (*LoadFamilyCallBackFunction)(gcmp::IDocument*, const std::wstring&);

    //////////////////////////////////////////////////////////////////////////
    //ClipBoard类：提供与剪切板相关，且紧密依赖于模型层的API操作
    //说明：
    //(1) 所有操作均不带事务处理
    //(2) 目前不进行文档改变的侦听，在粘贴时不合法会返回失败
    //////////////////////////////////////////////////////////////////////////
    class GBMP_MODEL_EXPORT ClipBoard : public IClipBoard, public IDocumentEventHandler
    {
    public:
        ~ClipBoard(void);

        static ClipBoard* Get();

        void On(IDocumentEventArgs* pArgs) override;

    public:
        //------------------------------------------------------------------------
        //前置：必需有待复制内容
        //返回：false表示复制不成功（业务上不合法）
        //------------------------------------------------------------------------
        bool Copy(IDocument* pDocFrom, const ElementId & viewIdFrom, const std::vector<ElementId>& elemIds) override;

        bool HasContent() const override { return m_pDocFrom && !m_elementIds.empty(); } 

        //------------------------------------------------------------------------
        //前置：有粘贴内容（即HasContent返回true）
        //返回：false表示粘贴不成功（业务上不合法，后续会增加粘贴位置等其它信息）
        //------------------------------------------------------------------------
        bool Paste(IDocument* pDocTo, const ElementId  & modelViewId, const Vector3d& pt, std::vector<ElementId>& pastedIds) override;

        //错误信息
        void SetErrorMessage(const std::wstring& errorMessage) override { m_lastErrorMessage = errorMessage; }
        const std::wstring& GetLastErrorMessage() override { return m_lastErrorMessage; }

    public:
        //------------------------------------------------------------------------
        //前置：复制粘贴立面图标高对象添加
        //返回：false表示粘贴不成功（业务上不合法，后续会增加粘贴位置等其它信息）
        //------------------------------------------------------------------------
        bool PasteElevationView(
            IDocument* pDocTo,
            ElementId modelViewId, 
            const Vector3d& pt, 
            std::vector<ElementId>& pastedIds);

        // 包围盒相关
        const Box3d& GetBoundingBox() const { return m_box3d; }

        // 剪切内容相关
        void ClearContent();
        const Vector3d& GetMinPt() const { return m_minPt; }
        const std::vector<Vector3d>& GetCorners() const { return m_corners; }

        // 插入点相关
        void SetInsertPt(const gcmp::Vector3d& pos) { m_insertPt = pos; }
        const Vector3d& GetInsertPt() const { return m_insertPt; }
        const Coordinate3d& GetLocalCoordinate() const { return m_localCoor; }

        // 在哪个视图上进行粘贴
        void SetModelViewId(ElementId pastedModelViewId) { m_pastedModelViewId = pastedModelViewId; }

        const IDocument* GetFromDocument() const { return m_pDocFrom; }
        ElementId GetFromLevelId() const { return m_levelIdFrom; }
        const std::set<ElementId>& GetCopyedElementIds() const { return m_elementIdsSet; }
        void SetLoadFamilyCallBack(LoadFamilyCallBackFunction callback) { m_loadFamilyCallBack = callback; }
        LoadFamilyCallBackFunction GetLoadFamilyCallBack() const { return m_loadFamilyCallBack; }

    public:
        void SetPasteToLevelIds(const std::vector<ElementId>& levelIdsTo) { m_levelIdsTo = levelIdsTo; }

    private:
        // 禁用以下操作调用(不要定义)
        ClipBoard();
        ClipBoard(const ClipBoard& copiedManager);
        ClipBoard& operator=(const ClipBoard& assignedManager);

        // 更新包围盒及相关数据
        Box3d RegenBoundingBox(const IDocument* pDoc, ElementId viewId, const std::vector<ElementId>& pastedIds) const;
        Box3d RegenBoundingBoxForSketch(const IDocument* pDoc, ElementId viewId, const Coordinate3d& coor, const std::vector<ElementId>& pastedIds) const;
        void CaculateCorners(const Box3d& box3d, std::vector<Vector3d>& corners);
   
    private:
        // 剪切板的数据
        IDocument* m_pDocFrom;
        ElementId m_levelIdFrom;
        std::vector<ElementId> m_elementIds;

        std::set<ElementId> m_elementIdsSet;
        // 缓存的数据(剪切数据包围盒的XYZ最小值，复制时会计算一次)
        // TODO: 即将删除
        Vector3d m_minPt;

        // 缓存的数据(剪切数据包围盒，复制时会计算一次)
        Box3d m_box3d;
        Coordinate3d m_localCoor;
        std::vector<Vector3d> m_corners;
        // 插入点(粘贴时以此点做为插入点，实际粘贴结果以此做相对偏移)
        // TODO：未来可能每个方向都需要有一个插入点(目前仅存储一个)
        Vector3d m_insertPt;

        // 错误信息
        std::wstring m_lastErrorMessage;

        // 在哪个视图上进行粘贴
        ElementId m_pastedModelViewId;

        // 保存复制源包围盒的最小的，只在复制时更新。粘贴时，如果无法计算包围盒，使用m_sourceOrgin。
        Vector3d m_sourceOrgin; 

        // 是否是在编辑模式中copy
        bool m_isEditMode;
        // 编辑模式类型
        std::wstring m_editModeType;
        // 原编辑模式中需要记录的信息
        std::vector<gcmp::ElementId> m_sourceElementIds;

        // TODO: GmModel里面需要调用一下GmModelFamily的LoadFamily函数（这是临时方案，未来会删除）
        // 现在由上层把GmModelFamily的函数指针设置到GmModel的接口中来解除直接依赖
        LoadFamilyCallBackFunction m_loadFamilyCallBack;

        std::vector<ElementId> m_levelIdsTo;

        OwnerPtr<IElementsChangeEventHandler> m_opElementChangeHanlder; //element删除处理
    };
}








